/*** In The Name of Allah ***/
package com.codeanalysissys.backend.python;

import com.codeanalysissys.backend.graphs.pdg.CDEdge;
import com.codeanalysissys.backend.graphs.pdg.ControlDependenceGraph;
import com.codeanalysissys.backend.graphs.pdg.PDNode;
import com.codeanalysissys.backend.python.parser.PythonLexer;
import com.codeanalysissys.backend.python.parser.PythonParser;
import com.codeanalysissys.backend.python.parser.PythonParserBaseVisitor;
import ghaffarian.graphs.Edge;
import ghaffarian.nanologger.Logger;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;

/**
 * Control Dependence Graph (CDG) builder for Python programs.
 * The CDG is actually a subgraph of the Program Dependence Graph (PDG).
 * This implementation is based on ANTLRv4's Visitor pattern.
 *
 * @author Seyed Mohammad Ghaffarian
 */
public class PythonCDGBuilder {

	public static ControlDependenceGraph build(InputStream inFile) throws IOException {
		ANTLRInputStream input = new ANTLRInputStream(inFile);
		PythonLexer lexer = new PythonLexer(input);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		PythonParser parser = new PythonParser(tokens);
		ParseTree tree = parser.root();
		ControlDependenceGraph cdg = new ControlDependenceGraph();
		ControlDependencyVisitor visitor = new ControlDependencyVisitor(cdg);
		visitor.visit(tree);
		return cdg;
	}

	public static ControlDependenceGraph build(File pythonFile) throws IOException {
		if (!pythonFile.getName().endsWith(".py"))
			throw new IOException("Not a Python File!");
		InputStream inFile = new FileInputStream(pythonFile);
		ANTLRInputStream input = new ANTLRInputStream(inFile);
		PythonLexer lexer = new PythonLexer(input);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		PythonParser parser = new PythonParser(tokens);
		ParseTree tree = parser.root();
		Logger.debug("CTRL DEP ANALYSIS: " + pythonFile.getPath());
		ControlDependenceGraph cdg = new ControlDependenceGraph(pythonFile.getName());
		ControlDependencyVisitor visitor = new ControlDependencyVisitor(cdg);
		visitor.visit(tree);
		return cdg;
	}

	private static class ControlDependencyVisitor extends PythonParserBaseVisitor<Void> {
		
		private ControlDependenceGraph cdg;
		private Deque<PDNode> ctrlDeps;
		private Deque<PDNode> negDeps;
		private Deque<Integer> jmpCounts;
		private Deque<PDNode> jumpDeps;
		private boolean buildRegion;
		private boolean follows;
		private int lastFollowDepth;
		private int regionCounter;
		private int jmpCounter;

		private Deque<String> classNames;
		public ControlDependencyVisitor(ControlDependenceGraph cdg) {
			this.cdg = cdg;
			ctrlDeps = new ArrayDeque<>();
			negDeps = new ArrayDeque<>();
			jumpDeps = new ArrayDeque<>();
			jmpCounts = new ArrayDeque<>();
			classNames = new ArrayDeque<>();
			buildRegion = false;
			follows = true;
			lastFollowDepth = 0;
			regionCounter = 1;
			jmpCounter = 0;
		}

		private void init() {
			ctrlDeps.clear();
			negDeps.clear();
			jumpDeps.clear();
			jmpCounts.clear();
			buildRegion = false;
			follows = true;
			lastFollowDepth = 0;
			regionCounter = 1;
			jmpCounter = 0;
		}

		// 解析方法即可
		@Override
		public Void visitClass_or_func_def_stmt(PythonParser.Class_or_func_def_stmtContext ctx) {
			//| decorator* (classdef | funcdef)
			if (ctx.classdef() != null){
				classNames.push(ctx.classdef().name().getText());
				visit(ctx.classdef());
				classNames.pop();
			}
			if (ctx.funcdef() != null){
				visit(ctx.funcdef());
			}
			return null;
		}

		// 不解析类，只解析函数，因此只需要记录类名就行了
		@Override
		public Void visitClassdef(PythonParser.ClassdefContext ctx) {
			//   : CLASS name (OPEN_PAREN arglist? CLOSE_PAREN)? COLON suite
			//    ;
			return visitChildren(ctx);
		}

		@Override
		public Void visitFuncdef(PythonParser.FuncdefContext ctx) {
			//  : ASYNC? DEF name OPEN_PAREN typedargslist? CLOSE_PAREN (ARROW test)? COLON suite
			//    ;
			try {
				init();
				// build the node
				PDNode entry = new PDNode();
				entry.setLineOfCode(ctx.getStart().getLine());
				String retType = "void";
				if (ctx.test() != null)     // set return type
					retType = ctx.test().getText();
				// set the node source code
				String precode = (ctx.ASYNC() != null) ? ctx.ASYNC().getText() + ctx.DEF().getText() + ' ' + ctx.name().getText() : ctx.DEF().getText() + ' ' + ctx.name().getText();
				precode = (ctx.typedargslist() != null) ? precode + '(' + ctx.typedargslist().getText() + ')' : precode + "()";
				precode = (ctx.ARROW() != null) ? precode + ctx.ARROW().getText() : precode;
				precode = (ctx.test() != null) ? precode + ctx.test().getText() + ctx.COLON().getText() : precode + ctx.COLON().getText();
				entry.setCode(precode);
				cdg.addVertex(entry);
				// push the entry node to CtrlDep stack
				pushCtrlDep(entry);
				if (ctx.suite() != null)
					visit(ctx.suite());
				// build the exit node
				PDNode exit = new PDNode();
				exit.setLineOfCode(0);
				exit.setCode("exit");
				cdg.addVertex(exit);
				//build the edge from entry to exit
				cdg.addEdge(new Edge<>(entry, new CDEdge(CDEdge.Type.EPSILON), exit));
				return null;
			}catch (Exception e){
				e.printStackTrace();
			}
			return visitChildren(ctx);
		}

		@Override
		public Void visitExpr_stmt(PythonParser.Expr_stmtContext ctx) {
			//: testlist_star_expr assign_part?
			PDNode declr = new PDNode();
			declr.setLineOfCode(ctx.getStart().getLine());
			declr.setCode(getOriginalCodeText(ctx));
			Logger.debug(declr.getLineOfCode() + ": " + declr.getCode());
			// add the sequence edge;the core is the last layer
			addNodeEdge(declr);
			return null;
		}

		@Override
		public Void visitIf_stmt(PythonParser.If_stmtContext ctx) {
			//IF cond=test COLON suite elif_clause* else_clause?
			PDNode ifNode = new PDNode();
			ifNode.setLineOfCode(ctx.getStart().getLine());
			String code = "if " + ctx.test().getText() + ctx.COLON().getText();
			ifNode.setCode(code);
			addNodeEdge(ifNode);                                    // add the edge from the last node to the if node
			/* deal with the true situation of if statement */
			PDNode thenRegion = new PDNode();
			thenRegion.setLineOfCode(0);
			thenRegion.setCode("THEN");
			cdg.addVertex(thenRegion);
			cdg.addEdge(new Edge<>(ifNode, new CDEdge(CDEdge.Type.TRUE), thenRegion));  // add the edge from if node to then node
			// build the else node
			PDNode elseRegion = new PDNode();
			elseRegion.setLineOfCode(0);
			elseRegion.setCode("ELSE");
			// deal with the first layer
			if (ctx.elif_clause() != null && ctx.elif_clause().size() != 0){
				int lastNodeid = 0;        // record the index of last elif node
				PDNode lastNode = new PDNode();
				for (int i = 0; i < ctx.elif_clause().size(); i++){
					PythonParser.Elif_clauseContext temctx = ctx.elif_clause(i);
					PDNode elifNode = new PDNode();
					elifNode.setLineOfCode(ctx.elif_clause(i).getStart().getLine());
					String elifcode = "elif " + temctx.test().getText() + temctx.COLON().getText();
					elifNode.setCode(elifcode);
					cdg.addVertex(elifNode);
					if (i == 0){
						cdg.addEdge(new Edge<>(ifNode, new CDEdge(CDEdge.Type.FALSE), elifNode));
						pushCtrlDep(thenRegion);
						negDeps.push(elifNode);
						visit(ctx.suite());           // 访问if的块
						negDeps.pop();
						popCtrlDep(thenRegion);        // 处理完了if中true的逻辑
						lastNode = elifNode; lastNodeid = i;
					}else{
						// not the first layer
						cdg.addEdge(new Edge<>(lastNode, new CDEdge(CDEdge.Type.FALSE), elifNode));    // build the elif node to elif node
						pushCtrlDep(thenRegion);
						negDeps.push(elifNode);
						if (ctx.elif_clause(i - 1) != null && ctx.elif_clause(i - 1).suite() != null)
							visit(ctx.elif_clause(i - 1).suite());           // 访问上一个elif的块
						negDeps.pop();
						popCtrlDep(thenRegion);        // 处理完了if中true的逻辑
						lastNode = elifNode; lastNodeid = i;
					}
				}
				pushCtrlDep(thenRegion);
				negDeps.push(lastNode);
				visit(ctx.elif_clause(lastNodeid).suite());
				negDeps.pop();
				popCtrlDep(thenRegion);
				if (ctx.else_clause() != null){
					// if - elif - else
					follows = false;
					cdg.addVertex(elseRegion);
					cdg.addEdge(new Edge<>(lastNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
					// 访问完上一个elif
					pushCtrlDep(lastNode);
					negDeps.push(thenRegion);
					visit(ctx.elif_clause(lastNodeid).suite());
					negDeps.pop();
					popCtrlDep(lastNode);
					// 访问else node
					pushCtrlDep(elseRegion);
					negDeps.push(thenRegion);
					visit(ctx.else_clause().suite());
					negDeps.pop();
					popCtrlDep(elseRegion);
				}else{
					// there is no else, but we need to add the ELSE region
					cdg.addVertex(elseRegion);
					cdg.addEdge(new Edge<>(lastNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
				}
			}else{
				// if-else structure
				pushCtrlDep(thenRegion);
				negDeps.push(elseRegion);
				visit(ctx.suite());
				if (negDeps != null && negDeps.size() != 0) {
					negDeps.pop();
				}
				popCtrlDep(thenRegion);
				if (ctx.else_clause() != null){
					follows = false;
					cdg.addVertex(elseRegion);
					cdg.addEdge(new Edge<>(ifNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
					//
					pushCtrlDep(elseRegion);
					negDeps.push(thenRegion);
					visit(ctx.else_clause().suite());
					negDeps.pop();
					popCtrlDep(elseRegion);
				}else if (buildRegion) {
					// there is no else, but we need to add the ELSE region
					cdg.addVertex(elseRegion);
					cdg.addEdge(new Edge<>(ifNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
				}
				follows = true;
			}
			return null;
		}

		@Override
		public Void visitFor_stmt(PythonParser.For_stmtContext ctx) {
			// ASYNC? FOR exprlist IN testlist COLON suite else_clause?
			// build the for node
			PDNode forNode = new PDNode();
			String code = (ctx.ASYNC() != null) ? "async for "+ctx.exprlist().getText()+" in "+ctx.testlist().getText()+":" : "for "+ctx.exprlist().getText()+" in "+ctx.testlist().getText()+":";
			forNode.setLineOfCode(ctx.getStart().getLine());
			forNode.setCode(code);
			addNodeEdge(forNode);   // add the edge from parent to forNode
			// add the loop region node
			PDNode loopRegion = new PDNode();
			loopRegion.setLineOfCode(0);
			loopRegion.setCode("LOOP");
			cdg.addVertex(loopRegion);
			cdg.addEdge(new Edge<>(forNode, new CDEdge(CDEdge.Type.TRUE), loopRegion));
			// deal with the true situation
			pushLoopBlockDep(loopRegion);
			visit(ctx.suite());
			popLoopBlockDep(loopRegion);
			// deal with the false situation
			PDNode elseRegion = new PDNode();
			elseRegion.setLineOfCode(0);
			elseRegion.setCode("ELSE");
			if (ctx.else_clause() != null) {
				follows = false;
				cdg.addVertex(elseRegion);
				cdg.addEdge(new Edge<>(forNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
				//
				pushCtrlDep(elseRegion);
				visit(ctx.else_clause().suite());
				popCtrlDep(elseRegion);
			}else{
				// there is no else, but we need to add the ELSE region
				cdg.addVertex(elseRegion);
				cdg.addEdge(new Edge<>(forNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
			}
			return null;
		}

		@Override
		public Void visitWhile_stmt(PythonParser.While_stmtContext ctx) {
			//WHILE test COLON suite else_clause?
			PDNode whileNode = new PDNode();
			whileNode.setLineOfCode(ctx.getStart().getLine());
			whileNode.setCode("while " + getOriginalCodeText(ctx.test()) + ctx.COLON().getText());
			addNodeEdge(whileNode);              // add the edge from parents node to whileNode
			// when it is the true situation
			PDNode loopRegion = new PDNode();
			loopRegion.setLineOfCode(0);
			loopRegion.setCode("LOOP");
			cdg.addVertex(loopRegion);
			cdg.addEdge(new Edge<>(whileNode, new CDEdge(CDEdge.Type.TRUE), loopRegion));  // add the edge from whileNode to loopNode
			// walk the while block
			pushLoopBlockDep(loopRegion);
			visit(ctx.suite());
			popLoopBlockDep(loopRegion);
			// deal with the false situation
			PDNode elseRegion = new PDNode();
			elseRegion.setLineOfCode(0);
			elseRegion.setCode("ELSE");
			if (ctx.else_clause() != null) {
				follows = false;
				cdg.addVertex(elseRegion);
				cdg.addEdge(new Edge<>(whileNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
				// walk the else namely false situation
				pushCtrlDep(elseRegion);
				visit(ctx.else_clause().suite());
				popCtrlDep(elseRegion);
			}else{
				// there is no else, but we need to add the ELSE region
				cdg.addVertex(elseRegion);
				cdg.addEdge(new Edge<>(whileNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
			}
			return null;
		}

		@Override
		public Void visitBreak_stmt(PythonParser.Break_stmtContext ctx) {
            // BREAK
			PDNode brk = new PDNode();
			brk.setLineOfCode(ctx.getStart().getLine());
			brk.setCode(getOriginalCodeText(ctx));
			addNodeEdge(brk);
			//
			// NOTE: an important assumption here is that 'break'
			//       is the last statement inside an if-else body,
			//       or it's the last statement inside a case-block
			if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
				jumpDeps.push(negDeps.peek());
				jumpDeps.peek().setProperty("isExit", Boolean.FALSE);
				jumpDeps.peek().setProperty("isJump", Boolean.TRUE);
				lastFollowDepth = ctrlDeps.size();
				buildRegion = true;
			}
			return null;
		}

		@Override
		public Void visitRaise_stmt(PythonParser.Raise_stmtContext ctx) {
			//| RAISE (test (COMMA test (COMMA test)?)?)? (FROM test)?
			PDNode raiseNode = new PDNode();
			raiseNode.setLineOfCode(ctx.getStart().getLine());
			if (ctx.test(0) != null) {
				String code = "raise " + ctx.test(0).getText() + " ";
				code = (ctx.FROM() != null) ? code + "from " + ctx.test(1).getText() : code;
				raiseNode.setCode(code);
			} else {
				String code = "raise";
				raiseNode.setCode(code);
			}
			addNodeEdge(raiseNode);
			// NOTE: an important assumption here is that 'throw'
			//       is the last statement inside an if-else body,
			//       or it's the last statement of a try-catch block,
			//       or it's the last statement of the entire method
			if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
				jumpDeps.push(negDeps.peek());
				jumpDeps.peek().setProperty("isExit", Boolean.TRUE);
				jumpDeps.peek().setProperty("isJump", Boolean.FALSE);
				lastFollowDepth = ctrlDeps.size();
				buildRegion = true;
			}
			return null;
		}

		@Override
		public Void visitTry_stmt(PythonParser.Try_stmtContext ctx) {
			// TRY COLON suite (except_clause+ else_clause? finally_clause? | finally_clause)
			PDNode tryNode = new PDNode();
			tryNode.setLineOfCode(ctx.getStart().getLine());
			tryNode.setCode("try:");
			tryNode.setProperty("isTry", Boolean.TRUE);
			addNodeEdge(tryNode);
			pushCtrlDep(tryNode);
			negDeps.push(tryNode);
			visit(ctx.suite());
			// visit each except situation
			if (ctx.except_clause() != null && ctx.except_clause().size() != 0){
				PDNode exceptNode;
				for(PythonParser.Except_clauseContext cx : ctx.except_clause()){
					exceptNode = new PDNode();
					exceptNode.setLineOfCode(ctx.getStart().getLine());
					if (cx.test() != null) {
						String code = "except " + cx.test().getText();
						code = (cx.AS() != null) ? code + " as " + cx.name().getText() + ":" : code + ":";
						exceptNode.setCode(code);                    // set the code
					} else {
						String code = "except:";
						exceptNode.setCode(code);                    // set the code
					}
					cdg.addVertex(exceptNode);
					cdg.addEdge(new Edge<>(tryNode, new CDEdge(CDEdge.Type.RAISE), exceptNode));
					pushCtrlDep(exceptNode);
					visit(cx.suite());
					popCtrlDep(exceptNode);
				}
			}
			// visit else block
			PDNode elseRegion = new PDNode();
			elseRegion.setLineOfCode(0);
			elseRegion.setCode("ELSE");
			if (ctx.else_clause() != null) {
				follows = false;
				cdg.addVertex(elseRegion);
				cdg.addEdge(new Edge<>(tryNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
				// walk the else namely false situation
				pushCtrlDep(elseRegion);
				visit(ctx.else_clause().suite());
				popCtrlDep(elseRegion);
			}else{
				// there is no else, but we need to add the ELSE region
				cdg.addVertex(elseRegion);
				cdg.addEdge(new Edge<>(tryNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
			}
			if (negDeps != null && negDeps.size() != 0) {
				negDeps.pop();    // pop tryNode
			}
			popCtrlDep(tryNode);           // pop tryNode
			// deal with the finally Node
			if (ctx.finally_clause() != null){
				//  FINALLY COLON suite
				PDNode finallyNode = new PDNode();
				finallyNode.setLineOfCode(ctx.finally_clause().getStart().getLine());
				finallyNode.setCode("finally");
				addNodeEdge(finallyNode); // add edge from parent node to finally
				pushCtrlDep(finallyNode);
				visit(ctx.finally_clause().suite());
				popCtrlDep(finallyNode);
			}
			// NOTE that Java does not allow a singular try-block (without catch or finally)
			return null;
		}

		@Override
		public Void visitWith_stmt(PythonParser.With_stmtContext ctx) {
			// ASYNC? WITH with_item (COMMA with_item)* COLON suite
			PDNode withNode = new PDNode();
			withNode.setLineOfCode(ctx.getStart().getLine());
			String code = (ctx.ASYNC()!=null) ? "async with " : "with ";
			code = code + tranWithItem(ctx.with_item(0)) + " ";
			if (ctx.with_item().size() > 1){
				for (int i = 1; i < ctx.with_item().size(); i++){
					code = code + ", " + tranWithItem(ctx.with_item(i));
				}
				code = code + ":";
			}
			withNode.setCode(code);
			addNodeEdge(withNode);    // add the edge from parent node to withnode
			pushCtrlDep(withNode);
			visit(ctx.suite());
			popCtrlDep(withNode);
			return null;
		}

		@Override
		public Void visitPrint_stmt(PythonParser.Print_stmtContext ctx) {
			PDNode printNode = new PDNode();
			printNode.setLineOfCode(ctx.getStart().getLine());
			printNode.setCode(getOriginalCodeText(ctx));
			Logger.debug(printNode.getLineOfCode() + ": " + printNode.getCode());
			// add the sequence edge;the core is the last layer
			addNodeEdge(printNode);
			return null;
		}

		@Override
		public Void visitDel_stmt(PythonParser.Del_stmtContext ctx) {
			PDNode delNode = new PDNode();
			delNode.setLineOfCode(ctx.getStart().getLine());
			delNode.setCode(getOriginalCodeText(ctx));
			Logger.debug(delNode.getLineOfCode() + ": " + delNode.getCode());
			// add the sequence edge;the core is the last layer
			addNodeEdge(delNode);
			return null;
		}

		@Override
		public Void visitPass_stmt(PythonParser.Pass_stmtContext ctx) {
			PDNode passNode = new PDNode();
			passNode.setLineOfCode(ctx.getStart().getLine());
			passNode.setCode(getOriginalCodeText(ctx));
			Logger.debug(passNode.getLineOfCode() + ": " + passNode.getCode());
			// add the sequence edge;the core is the last layer
			addNodeEdge(passNode);
			return null;
		}

		@Override
		public Void visitContinue_stmt(PythonParser.Continue_stmtContext ctx) {
			PDNode continueNode = new PDNode();
			continueNode.setLineOfCode(ctx.getStart().getLine());
			continueNode.setCode(getOriginalCodeText(ctx));
			addNodeEdge(continueNode);
			// NOTE: an important assumption here is that 'continue'
			//       is the last statement inside an if-else body
			if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
				jumpDeps.push(negDeps.peek());
				jumpDeps.peek().setProperty("isExit", Boolean.FALSE);
				jumpDeps.peek().setProperty("isJump", Boolean.TRUE);
				lastFollowDepth = ctrlDeps.size();
				buildRegion = true;
			}
			return null;
		}

		@Override
		public Void visitReturn_stmt(PythonParser.Return_stmtContext ctx) {
			//| RETURN testlist?
			PDNode retNode = new PDNode();
			retNode.setLineOfCode(ctx.getStart().getLine());
			retNode.setCode(getOriginalCodeText(ctx));
			addNodeEdge(retNode);
			// NOTE: an important assumption here is that 'return'
			//       is the last statement inside an if-else body
			//       or it's the last statement of the entire method
			if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
				jumpDeps.push(negDeps.peek());
				jumpDeps.peek().setProperty("isExit", Boolean.TRUE);
				jumpDeps.peek().setProperty("isJump", Boolean.FALSE);
				lastFollowDepth = ctrlDeps.size();
				buildRegion = true;
			}
			return null;
		}

		@Override
		public Void visitYield_stmt(PythonParser.Yield_stmtContext ctx) {
			//  : YIELD yield_arg?
			//    ;
			PDNode yieldNode = new PDNode();
			yieldNode.setLineOfCode(ctx.getStart().getLine());
			yieldNode.setCode(ctx.getText());
			Logger.debug(yieldNode.getLineOfCode() + ": " + yieldNode.getCode());
			// add the sequence edge;the core is the last layer
			addNodeEdge(yieldNode);
			return null;
		}

		@Override
		public Void visitAssert_stmt(PythonParser.Assert_stmtContext ctx) {
			//ASSERT test (COMMA test)?
			PDNode AsstNode = new PDNode();
			AsstNode.setLineOfCode(ctx.getStart().getLine());
			String code = "assert " + ctx.test(0).getText();
			for (int i = 1; i < ctx.test().size(); i++){
				code = code + ", " + ctx.test(i).getText();
			}
			AsstNode.setCode(code);
			Logger.debug(AsstNode.getLineOfCode() + ": " + AsstNode.getCode());
			// add the sequence edge;the core is the last layer
			addNodeEdge(AsstNode);
			return null;
		}

		@Override
		public Void visitGlobal_stmt(PythonParser.Global_stmtContext ctx) {
			//| GLOBAL name (COMMA name)*
			PDNode glbNode = new PDNode();
			glbNode.setLineOfCode(ctx.getStart().getLine());
			assert ctx.COMMA().size() == ctx.name().size() - 1;
			String code = "global " + ctx.name(0).getText();
			if (ctx.COMMA().size() != 0 && ctx.COMMA() != null){
				int comNum = ctx.COMMA().size();
				for (int i = 0; i < comNum; i++){
					code = code + ',' + ctx.name(i+1).getText();
				}
			}
			glbNode.setCode(code);
//			System.out.println(code);
			Logger.debug(glbNode.getLineOfCode() + ": " + glbNode.getCode());
			// add the sequence edge;the core is the last layer
			addNodeEdge(glbNode);
			return null;
		}

		@Override
		public Void visitExec_stmt(PythonParser.Exec_stmtContext ctx) {
			//{this.CheckVersion(2)}? EXEC expr (IN test (COMMA test)?)? {this.SetVersion(2);}
			PDNode execNode = new PDNode();
			execNode.setLineOfCode(ctx.getStart().getLine());
			String code = "exec " + ctx.expr().getText();
			if (ctx.IN() != null) code = code + " in " + ctx.test(0).getText();
			for (int i = 1; i < ctx.test().size(); i++){
				code = code + ", " + ctx.test(i).getText();
			}
			System.out.println(code);
			execNode.setCode(getOriginalCodeText(ctx));
			Logger.debug(execNode.getLineOfCode() + ": " + execNode.getCode());
			addNodeEdge(execNode);
			return null;
		}

		@Override
		public Void visitNonlocal_stmt(PythonParser.Nonlocal_stmtContext ctx) {
			//{this.CheckVersion(3)}? NONLOCAL name (COMMA name)* {this.SetVersion(3);}
			PDNode nonlocalNode = new PDNode();
			nonlocalNode.setLineOfCode(ctx.getStart().getLine());
			String code = "nonlocal " + ctx.name(0).getText();
			for (int i = 1; i < ctx.name().size(); i++){
				code = code + ", " + ctx.name(i).getText();
			}
			nonlocalNode.setCode(code);
			Logger.debug(nonlocalNode.getLineOfCode() + ": " + nonlocalNode.getCode());
			addNodeEdge(nonlocalNode);
			return null;
		}

		/**
		 * Add given node to the CD-subgraph and
		 * create a new CD-edge based on the last control-dependency.
		 */
		private void addNodeEdge(PDNode node) {
			checkBuildFollowRegion();   //judge and add the region node
			cdg.addVertex(node);
			cdg.addEdge(new Edge<>(ctrlDeps.peek(), new CDEdge(CDEdge.Type.EPSILON), node));
		}

		/**
		 * Check if a follow-region must be created;
		 * if so, create it and push it on the CTRL-dependence stack.
		 */
		private void checkBuildFollowRegion() {
			Logger.debug("FOLLOWS = " + follows);
			Logger.debug("BUILD-REGION = " + buildRegion);
			if (buildRegion && follows) {
				PDNode followRegion = new PDNode();
				followRegion.setLineOfCode(0);
				followRegion.setCode("FOLLOW-" + regionCounter++);
				cdg.addVertex(followRegion);
				// check to see if there are any exit-jumps in the current chain
				followRegion.setProperty("isJump", Boolean.TRUE);
				for (PDNode dep: jumpDeps)
					if ((Boolean) dep.getProperty("isExit")) {
						followRegion.setProperty("isJump", Boolean.FALSE);
						followRegion.setProperty("isExit", Boolean.TRUE);
					}
				if ((Boolean) followRegion.getProperty("isJump"))
					++jmpCounter;
				// connect the follow-region
				if (Boolean.TRUE.equals(jumpDeps.peek().getProperty("isTry"))) {
					PDNode jmpDep = jumpDeps.pop();
					if (!cdg.containsVertex(jmpDep))
						cdg.addVertex(jmpDep);
					cdg.addEdge(new Edge<>(jmpDep, new CDEdge(CDEdge.Type.NOT_THROWS), followRegion));
				} else {
					PDNode jmpDep = jumpDeps.pop();
					if (!cdg.containsVertex(jmpDep))
						cdg.addVertex(jmpDep);
					cdg.addEdge(new Edge<>(jmpDep, new CDEdge(CDEdge.Type.EPSILON), followRegion));
				}
				// if the jump-chain is not empty, remove all non-exit jumps
				if (!jumpDeps.isEmpty()) {
					for (Iterator<PDNode> itr = jumpDeps.iterator(); itr.hasNext(); ) {
						PDNode dep = itr.next();
						if (Boolean.FALSE.equals(dep.getProperty("isExit")))
							itr.remove();
					}
				}
				lastFollowDepth = 0;
				pushCtrlDep(followRegion);
			}
		}

		/**
		 * Push given node to the control-dependency stack.
		 */
		private void pushCtrlDep(PDNode dep) {
			ctrlDeps.push(dep);
			buildRegion = false;
		}

		/**
		 * Push this loop block region to the control-dependency stack
		 * and reset the jumps-counter for this loop-block.
		 */
		private void pushLoopBlockDep(PDNode region) {
			pushCtrlDep(region);
			jmpCounts.push(jmpCounter);
			jmpCounter = 0;
		}

		/**
		 * Pop out the last dependency off the stack and
		 * set the 'buildRegion' flag if necessary.
		 */
		private void popCtrlDep(PDNode dep) {
			ctrlDeps.remove(dep); //ctrlDeps.pop();
			buildRegion = !jumpDeps.isEmpty();
		}

		/**
		 * Pop out this loop-block region off the control stack
		 * and also pop off all jump-dependencies of this block.
		 */
		private void popLoopBlockDep(PDNode region) {
			for (Iterator<PDNode> itr = ctrlDeps.iterator(); jmpCounter > 0 && itr.hasNext(); ) {
				// NOTE: This iteration works correctly, even though ctrlDeps is a stack.
				//       This is due to the Deque implementation, which removes in LIFO.
				PDNode dep = itr.next();
				if (Boolean.TRUE.equals(dep.getProperty("isJump"))) {
					itr.remove();
					--jmpCounter;
				}
			}
			jmpCounter = jmpCounts.pop();
			lastFollowDepth = 0;
			popCtrlDep(region);
		}

		/**
		 * transfor the with statement to String
		 */
		private String tranWithItem(ParserRuleContext ctx){
			assert ctx instanceof PythonParser.With_itemContext;
			String code = ((PythonParser.With_itemContext) ctx).test().getText();
			if (((PythonParser.With_itemContext) ctx).AS() != null){
				code = code + " as " + ((PythonParser.With_itemContext) ctx).expr().getText();
			}
			return code;
		}

		/**
		 * Get the original program text for the given parser-rule context.
		 * This is required for preserving whitespaces.
		 */
		private String getOriginalCodeText(ParserRuleContext ctx) {
			int start = ctx.start.getStartIndex();
			int stop = ctx.stop.getStopIndex();
			Interval interval = new Interval(start, stop);
			return ctx.start.getInputStream().getText(interval);
		}

	}
	
}

